﻿using System;
using System.Data;
using System.Threading;
using System.Collections.Generic;
using CounterServerModel;
using CounterServerModel.Exceptions;
using CounterServerModel.Authentication;
using SmartCountersDeployer;

using System.Net;
using System.Net.Mail;

namespace SmartCountersTests
{
    public class Program
    {
        private static int s_TempAccountID1;
        private static int s_TempAccountID2;
        private static int s_TempFaultID;
        private static int s_TempUserID;

        public static void Main(string[] args)
        {
            ConsoleColor colorBackup = Console.ForegroundColor;

            CreateDB();
            TestAuthentication();
            TestValidateAuthentication();
            TestCreateUser();
            TestIsUserExists();
            TestCreateAccount();
            TestIsAccountExists();
            TestGetUserInformation();
            TestGetAccountInformation();
            TestGetAllAccounts();
            TestGetAllUsers();
            TestUpdateAccount();
            TestUpdateUser();
            TestUpdateUsage();
            TestCreateFault();
            TestGetFaultInformation();
            TestGetAllFaults();
            TestUpdateFault();
            TestWriteBillingInfo();
            TestReadBillingInfo();
            TestIsPriceSet();
            TestCreateBill();
            TestGetBill();
            TestGetMonthlyUsageSoFar();
            TestDeleteAccount();
            TestDeleteUser();

            Console.ForegroundColor = colorBackup;

            Console.WriteLine();
            Console.WriteLine("Testing finished.{0}Press any key to continue...", Environment.NewLine);
            Console.ReadLine();
        }

        private static void CreateDB()
        {
            DBDeployer dbDeployer = new DBDeployer("sa","12345678","localhost","CountersDB","CountersUser","123");
            dbDeployer.DeployDB();
        }

        private static void TestAuthentication()
        {
            AuthenticationService authService = new AuthenticationService();
            AuthenticatedUserCredentials authenticatedCredentialsCorrect = authService.Authenticate(new UserCredentials("admin", "12345678"));
            AuthenticatedUserCredentials authenticatedCredentialsIncorrect = authService.Authenticate(new UserCredentials("admin", "1234"));

            if (authenticatedCredentialsCorrect != null && authenticatedCredentialsCorrect.ID == 1 && authenticatedCredentialsCorrect.Password == "12345678" && authenticatedCredentialsIncorrect == null)
            {
                OutputPassed("TestAuthentication");
            }
            else
            {
                OutputFailed("TestAuthentication");
            }
        }

        private static void TestValidateAuthentication()
        {
            AuthenticationService authService = new AuthenticationService();
            AuthenticatedUserCredentials authenticatedCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            authService.ValidateAuthentication(authenticatedCredentials);

            OutputPassed("TestValidateAuthentication");
        }

        private static void TestGetUserInformation()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoProvider = new InformationReader();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            UserInfo adminInfo1 = infoProvider.GetUserInformation(authenticatedAdminCredentials, 1);
            UserInfo userInfo1 = infoProvider.GetUserInformation(authenticatedAdminCredentials, "TempUser");

            AuthenticatedUserCredentials authenticatedUserCredentials = authService.Authenticate(new UserCredentials("TempUser", "TempUserPassword"));
            UserInfo adminInfo2 = null;

            try
            {
                adminInfo2 = infoProvider.GetUserInformation(authenticatedUserCredentials, 1);
            }
            catch (NotEnoughPrivilagesException)
            {
            }

            UserInfo userInfo2 = infoProvider.GetUserInformation(authenticatedUserCredentials, s_TempUserID);

            if (adminInfo1.Username == "admin" && userInfo1.Username == "TempUser" && adminInfo2 == null && userInfo2.Username == "TempUser"
                && adminInfo1.AccountsAssociatedWith.Count == 0 && userInfo1.AccountsAssociatedWith.Count == 1 && userInfo2.AccountsAssociatedWith.Count == 1)
            {
                OutputPassed("TestGetUserInformation");
            }
            else
            {
                OutputFailed("TestGetUserInformation");
            }
        }

        private static void TestIsUserExists()
        {
            UserCredentials userCredentials = new UserCredentials("admin", "12345678");
            AuthenticationService authService = new AuthenticationService();
            AuthenticatedUserCredentials authenticatedCredentials = authService.Authenticate(userCredentials);

            InformationReader infoProvider = new InformationReader();
            if (infoProvider.IsUserExists(authenticatedCredentials, "TempUser") && !infoProvider.IsUserExists(authenticatedCredentials, "usernotexists"))
            {
                OutputPassed("TestIsUserExists");
            }
            else
            {
                OutputFailed("TestIsUserExists");
            }
        }

        private static void TestGetAccountInformation()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoProvider = new InformationReader();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            AccountInfo accountInfo1 = infoProvider.GetAccountInformation(authenticatedAdminCredentials, s_TempAccountID1);

            AuthenticatedUserCredentials authenticatedUserCredentials = authService.Authenticate(new UserCredentials("TempUser", "TempUserPassword"));
            AccountInfo accountInfo2 = infoProvider.GetAccountInformation(authenticatedUserCredentials, s_TempAccountID1);
            AccountInfo accountInfo3 = null;

            try
            {
                accountInfo3 = infoProvider.GetAccountInformation(authenticatedUserCredentials, s_TempAccountID2);
            }
            catch (NotEnoughPrivilagesException)
            {
            }

            if (accountInfo1.AccountID == s_TempAccountID1 && accountInfo1.UsersAssociatedWith.Count == 1 && accountInfo2.AccountID == s_TempAccountID1 && accountInfo2.UsersAssociatedWith.Count == 1 && accountInfo3 == null)
            {
                OutputPassed("TestGetAccountInformation");
            }
            else
            {
                OutputFailed("TestGetAccountInformation");
            }
        }

        private static void TestGetAllAccounts()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoProvider = new InformationReader();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            List<AccountInfo> accounts = infoProvider.GetAllAccounts(authenticatedAdminCredentials);

            if (accounts.Count == 2 && accounts[0].AccountID == s_TempAccountID1 && accounts[1].AccountID == s_TempAccountID2)
            {
                OutputPassed("TestGetAllAccounts");
            }
            else
            {
                OutputFailed("TestGetAllAccounts");
            }
        }

        private static void TestIsAccountExists()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoProvider = new InformationReader();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            bool account1Exists = infoProvider.IsAccountExists(authenticatedAdminCredentials, "Israel", "Ashdod", "Created1", 7, 7);
            bool account2Exists = infoProvider.IsAccountExists(authenticatedAdminCredentials, "Israel", "Ashdod", "Created2", 8, null);
            bool account3Exists = infoProvider.IsAccountExists(authenticatedAdminCredentials, "Israel", "Beer-Sheva", "eee", 5, null);

            if (account1Exists && account2Exists && !account3Exists)
            {
                OutputPassed("TestIsAccountExists");
            }
            else
            {
                OutputFailed("TestIsAccountExists");
            }
        }

        private static void TestCreateAccount()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoReader = new InformationReader();
            InformationUpdater infoUpdater = new InformationUpdater();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            AccountInfo account1 = new AccountInfo()
            {
                Country = "Israel",
                City = "Ashdod",
                Street = "Created1",
                House = 7,
                Apartment = 7,
                Phone = "7",
                Longitude = 0,
                Latitude = 0,
            };
            account1.UsersAssociatedWith.Add(s_TempUserID);
            s_TempAccountID1 = infoUpdater.CreateAccount(authenticatedAdminCredentials, account1);

            AccountInfo account2 = new AccountInfo()
            {
                Country = "Israel",
                City = "Ashdod",
                Street = "Created2",
                House = 8,
                Apartment = null,
                Phone = "8",
                Longitude = 1,
                Latitude = 1,
            };
            s_TempAccountID2 = infoUpdater.CreateAccount(authenticatedAdminCredentials, account2);

            bool account1Exists = infoReader.IsAccountExists(authenticatedAdminCredentials, "Israel", "Ashdod", "Created1", 7, 7);
            bool account2Exists = infoReader.IsAccountExists(authenticatedAdminCredentials, "Israel", "Ashdod", "Created2", 8, null);

            if (account1Exists && account2Exists)
            {
                OutputPassed("TestCreateAccount");
            }
            else
            {
                OutputFailed("TestCreateAccount");
            }
        }

        private static void TestDeleteAccount()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoReader = new InformationReader();
            InformationUpdater infoUpdater = new InformationUpdater();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            infoUpdater.DeleteAccount(authenticatedAdminCredentials, new AccountInfo()
            {
                Country = "Israel",
                City = "Tel-Aviv",
                Street = "Created1",
                House = 7,
                Apartment = 7
            });

            infoUpdater.DeleteAccount(authenticatedAdminCredentials, s_TempAccountID2);

            bool account1Exists = infoReader.IsAccountExists(authenticatedAdminCredentials, "Israel", "Ashdod", "Created1", 7, 7);
            bool account2Exists = infoReader.IsAccountExists(authenticatedAdminCredentials, "Israel", "Ashdod", "Created2", 8, null);

            if (!account1Exists && !account2Exists)
            {
                OutputPassed("TestDeleteAccount");
            }
            else
            {
                OutputFailed("TestDeleteAccount");
            }
        }

        private static void TestCreateUser()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoReader = new InformationReader();
            InformationUpdater infoUpdater = new InformationUpdater();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            UserInfo userInfo = new UserInfo("TempUser")
            {
                UserPermissions = eUserPermissions.EditAssociatedAccountInfo | eUserPermissions.EditOwnUserInfo | eUserPermissions.ReadAssociatedAccountInfo | eUserPermissions.ReadOwnBill | eUserPermissions.ReadOwnUserInfo,
                Phone = "TempPhone",
                MobilePhone = "TempMobilePhone",
                Email = "TempEmail",
                Password = "TempUserPassword"
            };

            s_TempUserID = infoUpdater.CreateUser(authenticatedAdminCredentials, userInfo);

            bool userExists = infoReader.IsUserExists(authenticatedAdminCredentials, "TempUser");

            if (userExists)
            {
                OutputPassed("TestCreateUser");
            }
            else
            {
                OutputFailed("TestCreateUser");
            }
        }

        private static void TestGetAllUsers()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoProvider = new InformationReader();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            List<UserInfo> users = infoProvider.GetAllUsers(authenticatedAdminCredentials);

            if (users.Count == 2 && users[0].ID == authenticatedAdminCredentials.ID && users[1].ID == s_TempUserID)
            {
                OutputPassed("TestGetAllUsers");
            }
            else
            {
                OutputFailed("TestGetAllUsers");
            }
        }

        private static void TestDeleteUser()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoReader = new InformationReader();
            InformationUpdater infoUpdater = new InformationUpdater();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            infoUpdater.DeleteUser(authenticatedAdminCredentials, s_TempUserID);

            bool userExists = infoReader.IsUserExists(authenticatedAdminCredentials, "TempUser");

            if (!userExists)
            {
                OutputPassed("TestDeleteUser");
            }
            else
            {
                OutputFailed("TestDeleteUser");
            }
        }

        private static void TestUpdateAccount()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoReader = new InformationReader();
            InformationUpdater infoUpdater = new InformationUpdater();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            AccountInfo accountInfo = infoReader.GetAccountInformation(authenticatedAdminCredentials, s_TempAccountID1);
            accountInfo.City = "Tel-Aviv";
            infoUpdater.UpdateAccount(authenticatedAdminCredentials, accountInfo);

            AccountInfo updatedAccountInfo = infoReader.GetAccountInformation(authenticatedAdminCredentials, s_TempAccountID1);
            if (updatedAccountInfo.City == "Tel-Aviv")
            {
                OutputPassed("TestUpdateAccount");
            }
            else
            {
                OutputFailed("TestUpdateAccount");
            }
        }

        private static void TestUpdateUser()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoReader = new InformationReader();
            InformationUpdater infoUpdater = new InformationUpdater();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            UserInfo userInfo = infoReader.GetUserInformation(authenticatedAdminCredentials, s_TempUserID);
            userInfo.Email = "UpdatedEmail";
            infoUpdater.UpdateUser(authenticatedAdminCredentials, userInfo);

            UserInfo updatedUserInfo = infoReader.GetUserInformation(authenticatedAdminCredentials, s_TempUserID);

            if (updatedUserInfo.Email == "UpdatedEmail")
            {
                OutputPassed("TestUpdateUser");
            }
            else
            {
                OutputFailed("TestUpdateUser");
            }
        }

        private static void TestUpdateUsage()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationUpdater infoUpdater = new InformationUpdater();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            infoUpdater.UpdateUsage(authenticatedAdminCredentials, s_TempAccountID1, 4.8);
            Thread.Sleep(2000);
            infoUpdater.UpdateUsage(authenticatedAdminCredentials, s_TempAccountID1, 5.2);

            string query = string.Format("SELECT TotalUsage FROM Usages WHERE AccountID = '{0}';", s_TempAccountID1);
            DataTable table = DBService.Instance.PerformQuery(query);
            if (table.Rows.Count == 2 && double.Parse(table.Rows[0]["TotalUsage"].ToString()) == 4.8 && double.Parse(table.Rows[1]["TotalUsage"].ToString()) == 5.2)
            {
                OutputPassed("TestUpdateUsage");
            }
            else
            {
                OutputFailed("TestUpdateUsage");
            }
        }

        private static void TestCreateFault()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationUpdater infoUpdater = new InformationUpdater();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            s_TempFaultID = infoUpdater.CreateFault(authenticatedAdminCredentials, s_TempAccountID1, "No water.");
            infoUpdater.CreateFault(authenticatedAdminCredentials, s_TempAccountID1, "Another fault.");

            DataTable table = DBService.Instance.PerformQuery("SELECT FaultID FROM Faults;");
            if (table.Rows.Count != 0 && s_TempFaultID == int.Parse(table.Rows[0]["FaultID"].ToString()))
            {
                OutputPassed("TestCreateFault");
            }
            else
            {
                OutputFailed("TestCreateFault");
            }
        }

        private static void TestGetFaultInformation()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoReader = new InformationReader();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            Fault fault = infoReader.GetFaultInformation(authenticatedAdminCredentials, s_TempFaultID);

            if (fault.FaultID == s_TempFaultID && fault.Description == "No water.")
            {
                OutputPassed("TestGetFaultInformation");
            }
            else
            {
                OutputFailed("TestGetFaultInformation");
            }
        }

        private static void TestGetAllFaults()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoReader = new InformationReader();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            List<Fault> faultList = infoReader.GetAllOpenFaults(authenticatedAdminCredentials);

            if (faultList[0].FaultID == s_TempFaultID && faultList[0].Description == "No water." && faultList[1].Description == "Another fault.")
            {
                OutputPassed("TestGetAllFaults");
            }
            else
            {
                OutputFailed("TestGetAllFaults");
            }
        }

        private static void TestUpdateFault()
        {
            AuthenticationService authService = new AuthenticationService();
            InformationReader infoReader = new InformationReader();
            InformationUpdater infoUpdater = new InformationUpdater();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            Fault fault1 = infoReader.GetFaultInformation(authenticatedAdminCredentials, s_TempFaultID);
            fault1.IsSolved = true;
            infoUpdater.UpdateFault(authenticatedAdminCredentials, fault1);
            Fault fault2 = infoReader.GetFaultInformation(authenticatedAdminCredentials, s_TempFaultID);

            if (fault2.FaultID == s_TempFaultID && fault2.IsSolved)
            {
                OutputPassed("TestUpdateFault");
            }
            else
            {
                OutputFailed("TestUpdateFault");
            }
        }

        private static void TestWriteBillingInfo()
        {
            AuthenticationService authService = new AuthenticationService();
            BillingService billingService = new BillingService();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            billingService.SetCurrentPrice(authenticatedAdminCredentials, 5, "Initial price.");
            billingService.SetCurrentPrice(authenticatedAdminCredentials, 10, "New price.");

            DataTable table = DBService.Instance.PerformQuery("SELECT PriceForLiter FROM BillingProperties;");
            
            if (table.Rows.Count == 2 && double.Parse(table.Rows[0]["PriceForLiter"].ToString()) == 5 && double.Parse(table.Rows[1]["PriceForLiter"].ToString()) == 10)
            {
                OutputPassed("TestWriteBillingInfo");
            }
            else
            {
                OutputFailed("TestWriteBillingInfo");
            }
        }

        private static void TestReadBillingInfo()
        {
            AuthenticationService authService = new AuthenticationService();
            BillingService billingService = new BillingService();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            BillingInfo billingInfo = billingService.GetCurrentPrice(authenticatedAdminCredentials);
            if (billingInfo.PriceForLiter == 10 && billingInfo.EndDate == null && billingInfo.Comments == "New price.")
            {
                OutputPassed("TestReadBillingInfo");
            }
            else
            {
                OutputFailed("TestReadBillingInfo");
            }
        }

        private static void TestIsPriceSet()
        {
            AuthenticationService authService = new AuthenticationService();
            BillingService billingService = new BillingService();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            bool priceSet = billingService.IsPriceSet(authenticatedAdminCredentials);
            if (priceSet)
            {
                OutputPassed("TestIsPriceSet");
            }
            else
            {
                OutputFailed("TestIsPriceSet");
            }
        }

        private static void TestCreateBill()
        {
            AuthenticationService authService = new AuthenticationService();
            BillingService billingService = new BillingService();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            Bill bill = billingService.CreateBill(authenticatedAdminCredentials, s_TempAccountID1, DateTime.Now.Year, DateTime.Now.Month);

            if (bill.SumPaid == 52)
            {
                OutputPassed("TestCreateBill");
            }
            else
            {
                OutputFailed("TestCreateBill");
            }
        }

        private static void TestGetBill()
        {
            AuthenticationService authService = new AuthenticationService();
            BillingService billingService = new BillingService();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            Bill bill = billingService.GetBill(authenticatedAdminCredentials, s_TempAccountID1, DateTime.Now.Year, DateTime.Now.Month);

            if (bill.AccountID == s_TempAccountID1 && bill.Year == DateTime.Now.Year && bill.Month == DateTime.Now.Month && bill.TotalUsage == 5.2 && bill.SumPaid == 52)
            {
                OutputPassed("TestGetBill");
            }
            else
            {
                OutputFailed("TestGetBill");
            }
        }

        private static void TestGetMonthlyUsageSoFar()
        {
            AuthenticationService authService = new AuthenticationService();
            BillingService billingService = new BillingService();

            AuthenticatedUserCredentials authenticatedAdminCredentials = authService.Authenticate(new UserCredentials("admin", "12345678"));
            double monthlyUsage = billingService.GetMonthlyUsageSoFar(authenticatedAdminCredentials, s_TempAccountID1);

            if (monthlyUsage == 5.2)
            {
                OutputPassed("GetMonthlyUsageSoFar");
            }
            else
            {
                OutputFailed("GetMonthlyUsageSoFar");
            }
        }

        private static void OutputPassed(string i_TestName)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("{0} passed.", i_TestName);
        }

        private static void OutputFailed(string i_TestName)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("{0} failed.", i_TestName);
        }
    }
}
